class Behaviour {

    private static allBehaviours: { [uuid: number]: Behaviour } = {};

    private static behaviourUuidIndex = 1;

    static getBehaviour(uuid: number) {
        return this.allBehaviours[uuid];
    }


    uuid = 0;

    constructor() {
        this.uuid = Behaviour.behaviourUuidIndex++;
        Behaviour.allBehaviours[this.uuid] = this;
    }


    $isDestroy = false;
    $isExecuteOnStart = false;
    //isChangableValueForPrefab = false;

    gameObject: GameObject = null;
    onClick: Function = null;


    onAddToGameObject() {

    }

    onStart() {

    }

    onUpdate(duringTime) {

    }

    onDestroy() {

    }

    onValueChange() {

    }
}

const SerializedField: (defaultValue?: any) => PropertyDecorator = (defaultValue: any) => {


    return (target, key) => {
        const clz = target.constructor;
        if (!clz['properties']) {
            clz['properties'] = [];
        }
        clz['properties'].push({ key: key, defaultValue: defaultValue });
    }
}

class ChangableValueForPrefab extends Behaviour {

    properties = [];
    $behaviours = [];
    //isChangableValueForPrefab = true;

    onAddToGameObject() {

        this.onValueChange();
    }


    parseFromData(data) {

        for (const item of data.properties) {
            let parameters = {};
            //console.log(item.name)            
            for (const key of Object.keys(item.parameter)) {
                parameters[key] = item.parameter[key];
                //console.log(parameters[key] + ' → ' + item.parameter[key]);
            }
            this.properties.push({
                name: item.name,
                parameter: parameters
            });
        }
    }

    toData() {

        const obj: any = {
            name: 'ChangableValueForPrefab',
            properties: this.properties
        }

        return obj;
    }

    onValueChange() {
        for (const item of this.properties) {
            const behaviour = this.gameObject.getBehaviour(eval(item.name));
            //console.log(behaviour);
            for (const key of Object.keys(item.parameter)) {
                behaviour[key] = item.parameter[key];
            }

        }
        //console.log(this);
    }
}


class Transform extends Behaviour {


    @SerializedField('')
    objName = '';
    @SerializedField(0)
    x = 0;
    @SerializedField(0)
    y = 0;
    @SerializedField(1)
    scaleX = 1;
    @SerializedField(1)
    scaleY = 1;
    @SerializedField(0)
    rotation = 0;
    @SerializedField(1)
    alpha = 1;

    $globalAlpha = 1;
    $localMatrix;
    $globalMatrix;

    constructor() {
        super();
        this.$localMatrix = new Matrix();
        this.$globalMatrix = new Matrix();

    }

    onAddToGameObject() {
        //console.log(this.gameObject.id)
        this.objName = this.gameObject.id;
    }

    calculateGlobalProperties() {
        if (!this.gameObject.parent) {
            return;
        }
        const parentTransform = this.gameObject.parent.getBehaviour(Transform);
        this.$localMatrix.updateFromDisplayObject(this.x, this.y, this.scaleX, this.scaleY, this.rotation);
        this.$globalMatrix = matrixAppendMatrix(this.$localMatrix, parentTransform.$globalMatrix);
        this.$globalAlpha = parentTransform.$globalAlpha * this.alpha;
    }

    onValueChange() {
        this.gameObject.id = this.objName;
    }

}

class ImageRenderer extends Behaviour {


    @SerializedField(null)
    image = null;

    onAddToGameObject() {
        this.gameObject.renderer = this;
    }

    onStart() {

    }


    onDraw(context) {

        const texture = getImage(this.image);

        if (texture) {
            context.drawImage(texture, 0, 0);
        }
    }

    hitTest(point) {
        const texture = getImage(this.image);

        if (!texture) {
            return null;
        }

        const rectangle = { x: 0, y: 0, width: texture.width, height: texture.height };
        const result = checkPointInRectangle(point, rectangle);
        if (result) {
            return this.gameObject;
        }
        else {
            return null;
        }
    }
}

class Character extends Behaviour {


    @SerializedField('')
    characterName = '';
    @SerializedField('')
    characterKind = '';

    @SerializedField(1)
    hpMax = 1;
    @SerializedField(1)
    hpNow = 1;
    //mp = 0;
    @SerializedField(1)
    attack = 1;
    @SerializedField(1)
    defence = 1;
    @SerializedField(1)
    strong = 1;
    @SerializedField(1)
    intelligence = 1;
    @SerializedField(1)
    agility = 1;

    //hasSkill = false;
    $skillButtonList = [];

    @SerializedField([])
    skillList = [];


    onStart() {

        this.$skillButtonList = core.getObjectById('skillButton').children;

        if (this.skillList.length == this.$skillButtonList.length) {
            if (this.skillList.length != 0 && this.$skillButtonList.length != 0) {
                for (let i = 0; i < this.$skillButtonList.length; i++) {

                    this.$skillButtonList[i].getBehaviour(SkillButton).word = core.getObjectById('skill').children[i].id;
                    this.$skillButtonList[i].getBehaviour(SkillButton).skill = core.getObjectById('skill').children[i].getBehaviour(Skill);

                }
            }
        } else if (this.characterKind == 'friend') {
            //throw new Error('skillList.length ！= skillButtonList.length');
        }

    }



}

class Button extends Behaviour {


    @SerializedField('')
    word = ''
    @SerializedField('red')
    color = 'red';
    @SerializedField(30)
    length;
    @SerializedField(10)
    width;
    @SerializedField(true)
    interactable = true;
    @SerializedField(false)
    hasImage = false;



    onAddToGameObject() {
        this.gameObject.renderer = this;
    }

    onStart() {

    }


    onDraw(context) {


        context.save();

        if (this.hasImage) {

        } else {
            context.beginPath();
            context.strokeStyle = this.color;
            //context.globalAlpha = 0.5;
            context.rect(0, 0, this.length, this.width);
            context.stroke();
            //context.globalAlpha = 1;
            context.closePath();
        }


        context.font = '30px Arial';
        context.fillStyle = 'red';
        context.fillText(this.word, 10, 35);
        context.restore();
    }

    hitTest(point) {
        const rectangle = { x: 0, y: 0, width: this.length, height: this.width };
        const result = checkPointInRectangle(point, rectangle);
        if (result && this.interactable) {
            //console.log(this);
            return this;
        }
        else {
            return null;
        }
    }
}

class HealthLineRenderer extends Behaviour {


    @SerializedField(100)
    hpNow = 100;
    @SerializedField(100)
    hpMax = 100;
    @SerializedField(null)
    parent;
    @SerializedField(100)
    length = 100;
    @SerializedField(10)
    width = 10;
    @SerializedField('red')
    color = 'red';
    // @SerializedField(true)
    // isBeginFromLeft = true;


    onAddToGameObject() {
        this.gameObject.renderer = this;
    }

    onStart() {

    }

    onDraw(context) {

        const character = core.getObjectById(this.parent).getBehaviour(Character);
        this.hpNow = character.hpNow;
        this.hpMax = character.hpMax;


        context.save();

        if (this.hpNow < 0) {
            this.hpNow = 0;
        } else if (this.hpNow > this.hpMax) {
            this.hpNow = this.hpMax;
        }

        const percent = this.hpNow / this.hpMax;

        if (percent >= 0.75) {
            this.color = 'green';
        } else if (percent >= 0.40) {
            this.color = 'yellow';
        } else {
            this.color = 'red';
        }


        context.fillStyle = '#2f4554';
        context.fillRect(0, 0, this.length, -this.width);
        this.drawCircle(0, -this.width / 2, this.width / 2, '#2f4554', context);             //left
        this.drawCircle(this.length, -this.width / 2, this.width / 2, '#2f4554', context);    //right


        context.fillStyle = this.color;
        //以矩形左下角为图形坐标零点
        context.fillRect(0, 0 - this.width * 0.1, percent * (this.length - 2), -(this.width * 0.8));
        //将血条两端绘制为半圆形
        if (this.hpNow != 0) {
            this.drawCircle(0, -this.width / 2, this.width * 0.8 / 2, this.color, context);             //left
            this.drawCircle(percent * this.length, -this.width / 2, this.width * 0.8 / 2, this.color, context);    //right
        }

        // let word = this.health + '/' + this.maxHealth;
        // context.fillStyle = 'black';
        // context.font = this.height + 'px Arial';
        // context.fillText(word, 0 + this.length * 1.1, 0);

        context.restore();
    }
    drawCircle(x, y, r, color, context) {
        context.beginPath();
        context.arc(x, y, r, 0, 2 * Math.PI, true);
        context.closePath();
        context.fillStyle = color;
        context.fill();
    }

    hitTest(point) {

    }
}

class WordRenderer extends Behaviour {


    @SerializedField('')
    height = '';
    @SerializedField('red')
    color = 'red';
    @SerializedField('')
    word = '';

    onAddToGameObject() {
        this.gameObject.renderer = this;
    }

    onStart() {

    }


    onDraw(context) {

        context.save();
        //context.globalAlpha = this.$globalAlpha;
        context.font = this.height + 'px Arial';
        context.fillStyle = this.color;
        context.fillText(this.word, 0, 0);
        context.restore();
    }

    hitTest(point) {
        const rectangle = { x: 0, y: 0, width: this.word.length * 15.2, height: this.height };
        const result = checkPointInRectangle(point, rectangle);
        if (result) {
            return this;
        }
        else {
            return null;
        }
    }
}

